#include "../Util/Util.h"
#include "ListBoxEx.h"
#include "../debug_new.h"

CListBoxEx::CListBoxEx()
{
	Init();
}

CListBoxEx::CListBoxEx(UINT nID):CControl(nID)
{
	Init();
}

CListBoxEx::CListBoxEx(wstring strCaption,UINT nID)
:	CControl(strCaption,nID)
{
	Init();
}

CListBoxEx::CListBoxEx(DWORD dwStyle,wstring strCaption, const CRect &rc, UINT nID)
:	CControl(dwStyle,strCaption, rc, nID)
{
	Init();
	m_nWndWidth = rc.Width();
	m_nWndHeight = rc.Height();
}

CListBoxEx::CListBoxEx(DWORD dwStyle, wstring strCaption, int x, int y, int cx, int cy, UINT nID)
:	CControl(dwStyle, strCaption, x, y, cx, cy, nID)
{
	Init();
	m_nWndWidth = cx;
	m_nWndHeight = cy;
}

CListBoxEx::~CListBoxEx()
{
	DeleteAllItems();
	if(m_hSelBackgnd)
	{
		DeleteObject(m_hSelBackgnd);
	}

	if(m_ImgLstCheck)
	{
		delete m_ImgLstCheck;
	}

	if(m_pImgLstIcon)
	{
		delete m_pImgLstIcon;
	}
}

void CListBoxEx::Init()
{
	m_nListHeight = 0;
	m_nPos = 0;
	m_nSelectedItem = 0;
	m_clrTxtNormal = RGB(0,0,0);
	m_clrTxtActive = RGB(255,255,255);
	m_dwExtendedStyles = LISTBOXEX_STYLE_GRIDLINES;
	m_hSelBackgnd = NULL;	
	m_ImgLstCheck = NULL;
	m_pImgLstIcon = NULL;
	m_nTotalPages = 0;
	m_nTextLeftMargin = 0;
	m_nPageCap = 0;
	m_nTotalPages = 1;
	m_nCurPage = 0;
	m_clrLineStart 	= RGB(68,68,68);
	m_clrLineEnd  	= RGB(255,255,255);  
}

void CListBoxEx::DeleteAllItems()
{
	m_nListHeight = 0;
	m_nTotalItems = 0;
	m_nPos = 0;

	list<LISTBOXEX_ITEM*>::iterator i,iEnd;
	iEnd = m_listItems.end();

	LISTBOXEX_ITEM *pItem = NULL;
	for (i = m_listItems.begin();i != iEnd; i++)
	{
		pItem = *i;
		delete pItem;
	}

	m_listItems.clear();
	m_mapItems.clear();
}


void CListBoxEx::Paint(CDC* pDC)
{
	if(m_rcControl.IsRectEmpty())
	{
		return ;
	}
	if (!m_bVisibale)
	{ 
		return ;
	}

	CRect rcItem = m_rcControl;
	rcItem.bottom = 0;
	int nStartItem = m_nPos;
	list<LISTBOXEX_ITEM*>::iterator i,iEnd;
	iEnd = m_listItems.end();
	i = m_listItems.begin();

	while(nStartItem-- && i!=iEnd )
	{
		i++;
	}

	int nCount = 0;

	for( ; i != iEnd && nCount < m_nPageCap  ; i++,nCount++)
	{
		LISTBOXEX_ITEM *pItem = *i;

		//Create the bounding rect for item
		rcItem.bottom = rcItem.top + pItem->nHeight;

		if(nCount + m_nPos == m_nSelectedItem)
		{
			//Call the virtual function for drawing the item
			DrawItem(pDC,rcItem,pItem,TRUE);
		}
		else
		{
			DrawItem(pDC,rcItem,pItem,FALSE);
		}

		pItem->rcItem = rcItem;
		//Move rcItem to next item
		rcItem.OffsetRect(0,pItem->nHeight);
	}
}

int CListBoxEx::InsertItem(wstring sText, UINT uiImage, int nStyle, int nHeight)
{
	LISTBOXEX_ITEM *pItem = new LISTBOXEX_ITEM;
	pItem->sItemText = sText;
	pItem->nStyle = nStyle;
	pItem->nItemNo = m_nTotalItems;
	pItem->uiImage = uiImage;

	pItem->nHeight = nHeight;
	pItem->bHeightSpecified = TRUE;

	m_nItemHeight = nHeight;

	m_listItems.push_back(pItem);

	m_nTotalItems ++;
	m_nListHeight += pItem->nHeight;

	m_mapItems[pItem->nItemNo] = pItem;

	CalPage();

	return (m_nTotalItems - 1);
}

LPARAM CListBoxEx::GetItemData(int nPos)
{
	LISTBOXEX_ITEM* pItem = GetInternalData(nPos);
	if(pItem)
		return pItem->lItemData;
	else
		return NULL;
}

void CListBoxEx::SetItemData(int nPos, LPARAM lItemData)
{
	LISTBOXEX_ITEM* pItem = GetInternalData(nPos);
	if(pItem)
	{
		pItem->lItemData = lItemData;
	}
}

void CListBoxEx::CalPage()
{
	if(0 != m_nWndHeight)
	{
		if(m_nListHeight <= m_nWndHeight)
		{
			m_nTotalPages = 1;
		}
		else if(m_nListHeight % m_nWndHeight)
		{
			m_nTotalPages = m_nListHeight / m_nWndHeight + 1;
		}
		else
		{
			m_nTotalPages = m_nListHeight / m_nWndHeight;
		}
	}

	/*if(m_nCurPage > m_nTotalPages)
	{
		m_nCurPage = m_nTotalPages;
	}*/

	if(0 != m_nListHeight && 0 != m_nItemHeight)
	{
		m_nPageCap = m_nWndHeight / m_nItemHeight;
	}
}

CRect CListBoxEx::GetItemRect(int nPos)
{
	LISTBOXEX_ITEM* pItem = GetInternalData(nPos);
	if(pItem)
	{
		return pItem->rcItem;
	}
	else
	{
		return NULL;
	}
}

int CListBoxEx::GetSelectedItem()
{
	return m_nSelectedItem;
}

wstring CListBoxEx::GetItemText(int nPos)
{
	LISTBOXEX_ITEM* pItem = GetInternalData(nPos);
	if(pItem)
	{
		return pItem->sItemText;
	}
	else
	{
		return L"";
	}
}

void CListBoxEx::SetExtendedStyle(DWORD dwExStyle)
{
	m_dwExtendedStyles = dwExStyle;

	ReArrangeWholeLayout();
}

DWORD CListBoxEx::GetExtendedStyle()
{
	return m_dwExtendedStyles;
}


void CListBoxEx::EnsureVisible(int nPos)
{
	if(nPos - m_nPos < 0 )
	{
		SetPos(nPos - GetPageCap());
	}
	else if(nPos - m_nPos >= GetPageCap())
	{
		SetPos(nPos);
	}
	else
	{
	}
}

void CListBoxEx::SetItemCheck(int nPos,BOOL bCheck)
{
	LISTBOXEX_ITEM* pItem = GetInternalData(nPos);
	if(pItem)
	{
		pItem->bChecked = bCheck;
	}
}

BOOL CListBoxEx::GetItemCheck(int nPos)
{
	LISTBOXEX_ITEM* pItem = GetInternalData(nPos);
	if(pItem)
	{
		return pItem->bChecked;
	}
	else
	{
		return FALSE;
	}
}

void CListBoxEx::SetItemText(int nPos, wstring sItemText,BOOL bCalculateHeight)
{
	LISTBOXEX_ITEM* pItem = GetInternalData(nPos);
	if(pItem)
	{
		pItem->sItemText = sItemText;
		if(bCalculateHeight)
		{
			pItem->bHeightSpecified = FALSE;
		}
		else
		{
		}
	}
}

BOOL CListBoxEx::DeleteItem(int nPos)
{
	list<LISTBOXEX_ITEM*>::iterator i,iEnd;
	iEnd = m_listItems.end();

	LISTBOXEX_ITEM *pItem = NULL;
	for(i = m_listItems.begin();i != iEnd;i++)
	{
		pItem = *i;
		if(pItem->nItemNo == nPos)
		{
			m_listItems.erase(i);

			if(m_nSelectedItem == pItem->nItemNo)
			{
				m_nSelectedItem = NONE_SELECTED;
			}

			m_nListHeight -= pItem->nHeight;

			delete pItem;
			m_nTotalItems --;

			ReArrangeItems();
			CalPage();

			return TRUE;
		}
	}
	return FALSE;
}

void CListBoxEx::ReArrangeItems()
{
	int nCount = 0;
	m_mapItems.clear();

	list<LISTBOXEX_ITEM*>::iterator i,iEnd;
	iEnd = m_listItems.end();

	for(i = m_listItems.begin(); i != iEnd; i++,nCount++)
	{
		LISTBOXEX_ITEM *pItem = *i;
		pItem->nItemNo = nCount;
		SetInternalData(nCount,pItem);
	}

}

void CListBoxEx::ReArrangeWholeLayout()
{
	m_nTotalItems = 0;
	m_nListHeight = 0;

	m_mapItems.clear();

	list<LISTBOXEX_ITEM*>::iterator i,iEnd;
	iEnd = m_listItems.end();
	int nCount = 0;

	for( i = m_listItems.begin();i != iEnd;i++,nCount++)
	{
		LISTBOXEX_ITEM *pItem = *i;
		pItem->nItemNo = nCount;
		m_nTotalItems ++;
		m_nListHeight += pItem->nHeight;
		SetInternalData(nCount,pItem);
	}
}

UINT CListBoxEx::GetImage(int nPos)
{
	LISTBOXEX_ITEM *pItem = GetInternalData(nPos);
	if(pItem)
	{
		return pItem->uiImage;
	}
	return 0;
}

void CListBoxEx::SetImage(int nPos, UINT uiImage)
{
	LISTBOXEX_ITEM *pItem = GetInternalData(nPos);
	if(pItem)
	{
		pItem->uiImage = uiImage;
	}
}

void CListBoxEx::DrawLine(CDC* pDc,CPoint ptStart,CPoint ptEnd)
{
	BYTE rStart = GetRValue(m_clrLineStart);
	BYTE gStart = GetGValue(m_clrLineStart);
	BYTE bStart = GetBValue(m_clrLineStart);

	BYTE rEnd = GetRValue(m_clrLineEnd);
	BYTE gEnd = GetRValue(m_clrLineEnd);
	BYTE bEnd = GetRValue(m_clrLineEnd);

	BYTE r,g,b;

   int nWidth = (ptEnd.x - ptStart.x)/2;
   int nTmp = nWidth + ptStart.x;

	for(int i= ptStart.x; i <= nTmp; i++)
	{
		r = rStart + ((i - ptStart.x) * (rEnd-rStart) / nWidth);
		g = gStart + ((i - ptStart.x) * (gEnd-gStart) / nWidth);
		b = bStart + ((i - ptStart.x) * (bEnd-bStart) / nWidth);

		pDc->SetPixel(i,ptStart.y,RGB(r,g,b));

		pDc->SetPixel(ptEnd.x-i+ptStart.x,ptStart.y,RGB(r,g,b));
	}	
}

void CListBoxEx::DrawItem(CDC *pDC, CRect rcItem, LISTBOXEX_ITEM *pItem, BOOL bSelected)
{
	COLORREF clrOld;
	CRect rcImage(0,0,0,0);

	BEGIN_FONT_SELECTION

	pDC->SetBkMode(TRANSPARENT);

	if(bSelected && m_hSelBackgnd)
	{
		CRect rcTmp = rcItem;
		/*rcTmp.right -= 5;
		rcTmp.DeflateRect(0,-3);
		rcTmp.OffsetRect(0,7);*/

		pDC->AlphaBlendEx(rcTmp,m_hSelBackgnd,CRect(0,0,320,50),150);

		clrOld = pDC->SetTextColor(m_clrTxtActive);
	}
	else
	{
		clrOld = pDC->SetTextColor(m_clrTxtNormal);
	}

	if(m_dwExtendedStyles & LISTBOXEX_STYLE_GRIDLINES/* && !bSelected*/)
	{
		CPoint ptStart;
		ptStart.x = rcItem.left;
		ptStart.y = rcItem.top + rcItem.Height();

		CPoint ptEnd;
		ptEnd.x = rcItem.right-5;
		ptEnd.y = ptStart.y;
		
		DrawLine(pDC,ptStart,ptEnd);
	}

	CPoint ptCheckBox = rcItem.TopLeft();

	if(m_dwExtendedStyles & LISTBOXEX_STYLE_CHECKBOX)
	{
		ptCheckBox.x += ITEM_PADDING_CHECKBOX_LEFT;
		ptCheckBox.y += rcItem.Height() / 2 - 8;
		ptCheckBox.x += ITEM_CHECKBOX_WIDTH;
	}

	if(m_dwExtendedStyles & LISTBOXEX_STYLE_IMAGES)
	{
		if(m_pImgLstIcon)
		{
			/*IMAGEINFO imgInfo = {0};
			m_pImgLstIcon->GetImageInfo(0,&imgInfo);
			rcImage = imgInfo.rcImage;*/

			CSize sz;

			m_pImgLstIcon->GetIconSize(sz);
			rcImage.bottom = sz.cy;
			rcImage.right = sz.cx;

			CPoint pt = ptCheckBox;
			pt.x += ITEM_IMAGE_PADDING_LEFT;
			pt.y = rcItem.top;
			pt.y += (rcItem.Height() - rcImage.Height())/2;

			m_pImgLstIcon->Draw(pDC,pItem->uiImage,pt);			
		}
	}

	if(pItem->nStyle == NORMAL_TEXT)
	{

		CRect rc = rcItem;

		if(rcImage.Width())
		{
			rc.DeflateRect(rcImage.Width() + ITEM_IMAGE_PADDING_LEFT + ITEM_IMAGE_PADDING_RIGHT,0,0,0);
		}

		if(m_dwExtendedStyles & LISTBOXEX_STYLE_CHECKBOX)
		{
			rc.left += ITEM_PADDING_LEFT + ITEM_CHECKBOX_WIDTH;
		}
		else
		{
			rc.left += ITEM_PADDING_LEFT;
		}

		if(!pItem->bHeightSpecified)
		{
			rc.top += ITEM_PADDING_TOP;
		}

		rc.OffsetRect(m_nTextLeftMargin,0);

		pDC->DrawText(pItem->sItemText.c_str(),pItem->sItemText.length(),&rc,
			m_dwAlign/*DT_LEFT|DT_WORDBREAK|DT_VCENTER*/);
	}
	else if(pItem->nStyle == SINGLE_LINE_TEXT)
	{
		CRect rc = rcItem;
		if(rcImage.Width())
		{
			rc.DeflateRect(rcImage.Width() + ITEM_IMAGE_PADDING_LEFT + ITEM_IMAGE_PADDING_RIGHT,0,0,0);
		}
		if(m_dwExtendedStyles & LISTBOXEX_STYLE_CHECKBOX)
		{
			rc.left += ITEM_PADDING_LEFT + ITEM_CHECKBOX_WIDTH;
		}
		else
		{
			rc.left += ITEM_PADDING_LEFT;
		}

		if(!pItem->bHeightSpecified)
			rc.top += ITEM_PADDING_TOP;

		//See if we can fit the text in one line
		//TCHAR szBuffer[_MAX_PATH];
		//memset(szBuffer,0,_MAX_PATH);

		//_tcscpy(szBuffer,pItem->sItemText);
		/*
		if(PathCompactPath(pDC->GetSafeHdc(),szBuffer,rc.Width()))
		{
		pDC->DrawText(szBuffer,_tcslen(szBuffer),&rc,
		DT_LEFT|DT_SINGLELINE|DT_VCENTER);	
		}
		else*/

		{
			wstring strTmp;
			TCHAR szID[10] = {0};
			_itot(pItem->nItemNo+1,szID,10);
			wstring strText = pItem->sItemText;
			CUtil::TrimPathLeft(strText);
			
			strTmp = szID;
			strTmp += L". ";
			strTmp += strText;
			pDC->DrawText(strTmp.c_str(),strTmp.length(),&rc,m_dwAlign); 

		}
	}

	pDC->SetTextColor(clrOld);

	END_FONT_SELECTION
}

LISTBOXEX_ITEM * CListBoxEx::GetInternalData(int nPos)
{
	LISTBOXEX_ITEM *pData = NULL;
	pData = m_mapItems[nPos];
	return pData;
}


void CListBoxEx::Click(DWORD dwClick, POINT pt)
{
	if(!IsEnabled())
	{
		return;
	}

	if(!IsVisible())
	{
		return;
	}

	if(!m_pContainer || !m_pContainer->m_hWnd)
	{
		return;
	}

	if(!m_rcControl.PtInRect(pt))
	{ 
		m_nState = STATE_NORMAL;
		return;
	}
	else
	{

		BOOL bItemSelected = FALSE;
		//	m_nSelectedItem = 0;//NONE_SELECTED;
		int nStartItem = m_nPos;

		list<LISTBOXEX_ITEM*>::iterator i,iEnd;
		iEnd = m_listItems.end();
		i = m_listItems.begin();

		while(nStartItem--)
		{
			i++;
		}

		nStartItem = m_nPos;  
		int nCount = 0;

		for( ; i != iEnd && nCount < m_nPageCap  ; i++,nCount++)
		{
			LISTBOXEX_ITEM *pItem = *i;
			{
				if(pItem->rcItem.PtInRect(pt))
				{
					m_nSelectedItem = nStartItem + nCount;	
					bItemSelected = TRUE;		
				}
			}
		}

		if(dwClick & CLICK_DOWN)
		{			
			m_nState = STATE_PUSHED;
		}
		else if(dwClick & CLICK_UP)
		{
			if(m_pContainer->m_hWnd && bItemSelected)
			{
				CUtil::PlayBeepSound();
				PostMessage(m_pContainer->m_hWnd,WM_COMMAND, m_nID,NULL);
			}
			m_nState = STATE_NORMAL;
		}
	}
}

void CListBoxEx::SetPos(int nPos)
{
	if(0 >= nPos)
	{
		m_nPos = 0;
	}
	//else if(nPos > GetItemCount() - GetPageCap())
	//{
	//	m_nPos = GetItemCount() - GetPageCap();
	//}
	else if (nPos >= m_nTotalItems)
	{
		m_nPos = m_nTotalItems - 1;
	}
	else
	{
		m_nPos = nPos;
	}
}